package org.awa.demo.opencv.gltf;

import org.awa.demo.opencv.bean.MaterialInfo;
import org.awa.demo.opencv.bean.NoiseSource;
import org.awa.demo.opencv.bean.gltf.PointItemDto;
import org.awa.demo.opencv.bean.newgltf.NewPointItemDto;
import org.geotools.geometry.jts.JTS;
import org.geotools.referencing.CRS;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.io.WKTReader;
import org.locationtech.jts.math.Vector3D;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;

import java.util.List;

public class SoundUtil {

    public static float[] createPoint(float x, float y, float z) {
        float[] point = new float[3];
        point[0] = x;
        point[1] = y;
        point[2] = z;
        return point;
    }

    //换算回Gltf的左手坐标系，gltf是左手坐标系，分格时容易映射错。前端渲染时还是用gltf的左手坐标系
    public static float[] toGltfCrs(float[] blenderCrs){
        float[] result = new float[3];
        result[0] = blenderCrs[0];//x
        result[1] = blenderCrs[2];//y
        result[2] = -blenderCrs[1];//z
        return result;
    }

    public static Vector3D toGltfCrs(Vector3D blenderCrs){
        Vector3D result = new Vector3D(blenderCrs.getX(), blenderCrs.getZ(), -blenderCrs.getY());
        return result;
    }

    public static String toPointString(float[] point){
        return String.format("[%f,%f,%f]",point[0],point[1],point[2]);
    }

    public static String toPointString(Vector3D point){
        return String.format("[%f,%f,%f]",point.getX(),point.getY(),point.getZ());
    }

    //换算回blender的右手坐标系，gltf是左手坐标系，分格时容易映射错。前端渲染时还是用gltf的左手坐标系
    public static float[] toBlenderCrs(float[] gltfCrs){
        float[] result = new float[3];
        result[0] = gltfCrs[0];//x
        result[1] = -gltfCrs[2];//y
        result[2] = gltfCrs[1];//z
        return result;
    }

    //换算回blender的右手坐标系，gltf是左手坐标系，分格时容易映射错。前端渲染时还是用gltf的左手坐标系
    public static Vector3D toBlenderCrs(Vector3D gltfCrs){
        Vector3D result = new Vector3D(gltfCrs.getX(), -gltfCrs.getZ(), gltfCrs.getY());
        return result;
    }

    public static Double getDistance(PointItemDto beginPoint,PointItemDto endPoint) {
        return Math.sqrt(Math.pow(beginPoint.getX() - endPoint.getX(), 2) + Math.pow(beginPoint.getY() - endPoint.getY(), 2) + Math.pow(beginPoint.getZ() - endPoint.getZ(), 2));
    }

    public static Double getDistance(NewPointItemDto beginPoint,NewPointItemDto endPoint) {
        return Math.sqrt(Math.pow(beginPoint.getX() - endPoint.getX(), 2) + Math.pow(beginPoint.getY() - endPoint.getY(), 2) + Math.pow(beginPoint.getZ() - endPoint.getZ(), 2));
    }

    public static Double getDistance(Vector3D beginPoint,Vector3D endPoint) {
        return Math.sqrt(Math.pow(beginPoint.getX() - endPoint.getX(), 2) + Math.pow(beginPoint.getY() - endPoint.getY(), 2) + Math.pow(beginPoint.getZ() - endPoint.getZ(), 2));
    }

    /**
     * 噪声点位 叠加
     *
     * @param soundList
     * @return
     */
    public static Double soundSuperposition(List<Double> soundList){
        if(soundList == null || soundList.size() == 0){
            return 0d;
        }
        if(soundList.size() == 1){
            return soundList.get(0);
        }
        double totalLeqt = 0d;
        for (int i = 0; i < soundList.size(); i++) {
            totalLeqt += Math.pow(10,0.1 * soundList.get(i));
        }
        return 10 * Math.log10(totalLeqt);
    }

    public static PointItemDto getPointBean(float[] point) {
        PointItemDto pointItem = new PointItemDto();
        pointItem.setX(point[0]);
        pointItem.setY(point[1]);
        pointItem.setZ(point[2]);
        return pointItem;
    }

    public static NewPointItemDto getPointBean(Vector3D point) {
        NewPointItemDto pointItem = new NewPointItemDto();
        pointItem.setX(point.getX());
        pointItem.setY(point.getY());
        pointItem.setZ(point.getZ());
        return pointItem;
    }

    /**
     *
     * 等效声级根据距离衰减 计算 线声源
     *
     * @param distance
     * @param leqt
     * @return
     */
    public static Double calAttenuationLeptPointBase(double distance,double leqt){
        if(distance == 0){
            return leqt;
        }
        try{
            //因为计算认为为无限长线声源本身的声音。而衰减公式是两个距离之间的比值(r / r0)，故假设r0为0.01米，即1cm的距离
            return leqt - 20d * Math.log10(distance);
        }catch (Exception e){
            e.printStackTrace();
            return 0d;
        }
    }

    /**
     *
     * 等效声级根据距离衰减 计算 线声源
     *
     * @return
     */
    public static double calAttenuationLeptPointInDoor(double distance, NoiseSource noiseSource){
        if(distance <= 0.5){
            return noiseSource.getLw();
        }
        try{
            double f1 = noiseSource.getQ() / (4 * Math.PI * distance * distance);
            return noiseSource.getLw() + 10 * Math.log10(f1 + noiseSource.get4R());
        }catch (Exception e){
            e.printStackTrace();
            return 0d;
        }
    }

    /**
     *
     * 计算R值
     *
     * @return
     */
    public static double calInDoorR(List<MaterialInfo> materialList){
        double totalRate = 0;
        double totalArea = 0;
        for (int i = 0; i < materialList.size(); i++) {
            MaterialInfo materialInfo = materialList.get(i);
            totalRate += materialInfo.getArea() * materialInfo.getSoundAbsorption();
            totalArea += materialInfo.getArea();
        }
        double a = totalRate / totalArea;

        return totalArea * a / (1 - a);
    }

    //获取各网格的中心点（2维平面线）
    public static float[] getPointListByLine(float firstValue,float endValue,int num){
        float widthDistance = endValue - firstValue;
        float widthStep = widthDistance / (float)num;
        float halfWidth = widthStep / 2;
        float[] widthPoint = new float[num];
        for (int i = 0; i < num; i++) {
            widthPoint[i] = firstValue + (float)i * widthStep + halfWidth;
        }
        return widthPoint;
    }

    //获取各网格的中心点（2维平面线）
    public static double[] getPointListByLine(double firstValue,double endValue,int num){
        double widthDistance = endValue - firstValue;
        double widthStep = widthDistance / (double)num;
        double halfWidth = widthStep / 2;
        double[] widthPoint = new double[num];
        for (int i = 0; i < num; i++) {
            widthPoint[i] = firstValue + (double)i * widthStep + halfWidth;
        }
        return widthPoint;
    }

    //根据wkt生成geometry
    public static Geometry createGeometryFromWKT(String wkt) throws Exception{
        WKTReader reader = new WKTReader();
        Geometry geometry = reader.read(wkt);
        return geometry;
    }

    /**
     * 转换为墨卡托坐标系
     * 所gis元素操作，都需要使用墨卡托坐标系，普通坐标系以度为单位，没用。墨卡托以米为单位
     *
     * @param geometry
     * @param crs
     * @return
     * @throws Exception
     */
    public static Geometry getMercatorGeometry(Geometry geometry,String crs,String targetCrs) throws Exception{
        CoordinateReferenceSystem sourceCRS = CRS.decode(crs);
        CoordinateReferenceSystem targetCRS = CRS.decode(targetCrs);
        MathTransform transform = CRS.findMathTransform(sourceCRS, targetCRS, false);
        return JTS.transform(geometry, transform);
    }

    /**
     * 根据cgc2000坐标系获取墨卡托系对应的投影坐标系，目前只支持中国境内
     *
     * @param point
     * @return
     */
    public static String getMercatorCrs(Point point){
        double longitude = point.getX();
        if(longitude >= 133.5){
            return "EPSG:4554";
        }else if(longitude >= 130.5){
            return "EPSG:4553";
        }else if(longitude >= 127.5){
            return "EPSG:4552";
        }else if(longitude >= 124.5){
            return "EPSG:4551";
        }else if(longitude >= 121.5){
            return "EPSG:4550";
        }else if(longitude >= 118.5){
            return "EPSG:4549";
        }else if(longitude >= 115.5){
            return "EPSG:4548";
        }else if(longitude >= 112.5){
            return "EPSG:4547";
        }else if(longitude >= 109.5){
            return "EPSG:4546";
        }else if(longitude >= 106.5){
            return "EPSG:4545";
        }else if(longitude >= 103.5){
            return "EPSG:4544";
        }else if(longitude >= 100.5){
            return "EPSG:4543";
        }else if(longitude >= 97.5){
            return "EPSG:4542";
        }else if(longitude >= 94.5){
            return "EPSG:4541";
        }else if(longitude >= 91.5){
            return "EPSG:4540";
        }else if(longitude >= 88.5){
            return "EPSG:4539";
        }else if(longitude >= 85.5){
            return "EPSG:4538";
        }else if(longitude >= 82.5){
            return "EPSG:4537";
        }else if(longitude >= 79.5){
            return "EPSG:4536";
        }else if(longitude >= 76.5){
            return "EPSG:4535";
        }else{
            return "EPSG:4534";
        }
    }
}
